package com.chenyun.idemlimit.aspect;

import cn.dev33.satoken.SaManager;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSON;
import com.chenyun.common.context.ServletContext;
import com.chenyun.common.context.SpringContext;
import com.chenyun.common.exceptions.BusinessException;
import com.chenyun.common.vo.AjaxResult;
import com.chenyun.idemlimit.annotation.RepeatSubmit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;

/**
 * 重复提交
 *
 * @author: Lonni
 * @date: 2024/1/22 0022 11:04
 */
@Aspect
@Slf4j
public class RepeatSubmitAspect implements Serializable {

    static final String repeat_key = "repeat_submit:";
    private final InheritableThreadLocal<String> REPEAT_LOCAL = new InheritableThreadLocal<>();

    public RepeatSubmitAspect() {
        log.info("进入防重复提交....");
    }


    @Around("@annotation(repeatSubmit)")
    public void Around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
        long time = repeatSubmit.unit().toMillis(repeatSubmit.time());
        Object[] args = joinPoint.getArgs();
        StringJoiner joiner = new StringJoiner(",");
        String paramValue = "";
        //将参数拼装
        if (ArrayUtil.isNotEmpty(args)) {
            for (Object o : args) {
                if (ObjectUtil.isNotNull(o) && !isMultipartFile(o)) {
                    joiner.add(JSON.toJSONString(o));
                }
            }
        }
        HttpServletRequest request = ServletContext.getRequest();
        String url = request.getRequestURI();
        String tokenName = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));
        String submitKey = SecureUtil.md5(tokenName + ":" + joiner.toString());
        //key值 :   repeat_submit:url+submitKey
        String key = repeat_key + url + submitKey;
        log.info("防重提交的key:{}",key);
        RedissonClient client = SpringContext.getBean(RedissonClient.class);
        RBucket<Object> bucket = client.getBucket(key);
        boolean b = bucket.setIfAbsent("", Duration.ofMillis(time));
        if (b) {
            REPEAT_LOCAL.set(key);
        } else {
            throw new BusinessException(repeatSubmit.msg());
        }
        try {
            Object proceed = joinPoint.proceed();
            if (proceed instanceof AjaxResult) {
                AjaxResult result = (AjaxResult) proceed;
                if (!result.isSuccess()) {
                    bucket.delete();
                }
            }
            REPEAT_LOCAL.remove();
        } catch (Throwable exception) {
            REPEAT_LOCAL.remove();
            bucket.delete();
        }

    }


    /**
     * 判断是不是 文件对象
     *
     * @param o
     * @return
     */
    boolean isMultipartFile(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.values()) {
                return value instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }


}



